﻿using System.Collections.Generic;
using Anderson.NetLobby.Common;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System;
using Anderson.NetLobby.MessageContent;
using System.Text;

//TODO; 1; Read port and max from config file.

//DONE; 2; Send To All Player SERVER_L3_ROOM_LIST_CHANGED
//      -- room Create
//      -- room Enter, Exit, Start, RoomStart
//      -- room Close

//REMOVED; 3;

//DONE; 4; 客户端没有收到id之前，不允许发送内容
//      DONE; 4.1 获取房间列表
//      DONE; 4.2 创建房间
//      DONE; 4.3 加入房间
//      DONE; 4.4 房间列表变化
//      DONE; 4.5 心跳信息

namespace Anderson.NetLobby.Server
{
    public class NetLobbyServer
    {
        #region Properties
        private INetLobbyLog logger = null;
        private Socket serverSocket = null;

        private IDictionary<long, NetLobbySession> playerSessions;
        private IDictionary<long, NetLobbyRoom> rooms;
        private bool roomChanged = false;

        private object roomChangedLock = new object();
        private object roomsLock = new object();
        //什么时候Lock Sessions?
        //  1. 有Session加入时
        //  2. 服务器广播信息时
        //  3. 有Session断开连接时
        private object sessionsLock = new object();

        private int serverPort;
        private int maxPlayerCount;
        private int maxRoomCount;
        private int roomMinPlayer = 2;

        private SemaphoreSlim playerCountSemaphore;
        public static ManualResetEvent allDone = new ManualResetEvent(false);

        //Frame
        private long m_frameCount = 0;
        private int threadSleep = 5;
        private int heartTimes = 1000;//发送一次心跳信息频率(ms)
        private DateTime StartTime = DateTime.Now;
        #endregion

        #region Constructor
        public NetLobbyServer(int port, int maxPlayer, int maxRoom, INetLobbyLog logger)
        {
            this.logger = logger;

            serverPort = port;
            maxPlayerCount = maxPlayer;
            maxRoomCount = maxRoom;

            playerCountSemaphore = new SemaphoreSlim(maxPlayerCount);

            playerSessions = new Dictionary<long, NetLobbySession>();
            rooms = new Dictionary<long, NetLobbyRoom>();

            StartServer();
        }        
        #endregion

        #region Properties
        public long FrameCount
        {
            get
            {
                return m_frameCount;
            }

            set
            {
                m_frameCount = value;
            }
        }
        #endregion

        #region Func
        private void StartServer()
        {
            IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, serverPort);

            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                serverSocket.Bind(ipEndPoint);
                serverSocket.Listen(100);

                StartTime = DateTime.Now;
                lastSendHeartTime = StartTime;
                FrameCount = 0;

                new Thread( () => TimeToSend() ).Start();

                new Thread( () => AcceptThreadFunc() ).Start();
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }

        }

        private void AcceptThreadFunc()
        {
            while (!cancelToket.IsCancellationRequested)
            {
                allDone.Reset();

                serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), serverSocket);

                allDone.WaitOne();
            }

            logger.Log("Server Stoped. Stop Accept.");
        }

        public void StopServer(Action action)
        {
            logger.Log("StopServer");

            cancelToket.Cancel();
            allDone.Set();//防止接收客户端连接的方法AcceptThreadFunc阻塞

            //DONE; server close
            try
            {
                serverSocket.Shutdown(SocketShutdown.Both);                
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
            try
            {
                serverSocket.Close();
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
            

            //Remove All Room
            int roomCount = rooms.Count;
            CountdownEvent coundDown = new CountdownEvent(roomCount);
            foreach (NetLobbyRoom room in rooms.Values)
            {
                room.CloseRoom( () => { coundDown.Signal(); });
            }
            coundDown.Wait();

            rooms.Clear();
            rooms = null;

            //Close All Session.Socket
            foreach (NetLobbySession session in playerSessions.Values)
            {
                session.ServerSideClose(playerCountSemaphore);
            }
            playerSessions.Clear();
            playerSessions = null;

            //done.
            serverSocket = null;

            action();
        }
        #endregion

        #region CallBack.Accept
        private void AcceptCallback(IAsyncResult ar)
        {
            logger.Log("Server - AcceptCallback.");

            NetLobbySession session = null;

            try
            {
                playerCountSemaphore.Wait();

                Socket listener = (Socket)ar.AsyncState;
                Socket handler = listener.EndAccept(ar);

                session = new NetLobbySession(handler, "", 0, DateTime.Now, logger);
                playerSessions.Add(session.playerId, session);

                SendPlayerId(session);
                
                handler.BeginReceive(session.buffer, 0, NetLobbySession.BufferSize, SocketFlags.None, new AsyncCallback(ReadCallback), session);
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);

                if (null != session && playerSessions.ContainsKey(session.playerId))
                {
                    playerSessions.Remove(session.playerId);
                }

                playerCountSemaphore.Release();
            }
            finally
            {
                allDone.Set();
            }
            
        }
        #endregion

        #region CallBack.Read
        public void ReadCallback(IAsyncResult ar)
        {
            logger.Log("Server - ReadCallback");

            NetLobbySession session = (NetLobbySession)ar.AsyncState;
            Socket handler = session.playerSocket;

            bool socketDisconnectOrError = false;
            try
            {
                int bytesRead = handler.EndReceive(ar);

                if (bytesRead > 0)
                {
                    byte[] readBytes = new byte[bytesRead];
                    Buffer.BlockCopy(session.buffer, 0, readBytes, 0, bytesRead);
                    Buffer.SetByte(session.buffer, 0, 0);//并不需要手动清空

                    byte[] fullMessageBytes = session.ReceiveMessage(readBytes);
                    if (null != fullMessageBytes)
                    {
                        AnalyzeNetMessage(fullMessageBytes);
                    }
                }
                else
                {
                    socketDisconnectOrError = true;
                }
            }
            catch (SocketException e)
            {
                logger.LogError(e.Message);
                socketDisconnectOrError = true;
            }

            if (socketDisconnectOrError)
            {
                logger.Log("Client Disconnect.");

                session.ServerSideClose(playerCountSemaphore);

                if (session.playerInRoom)
                {
                    RoomExitWithIds(session.roomId, session.playerId);
                }//if player in room

                lock (sessionsLock)
                {
                    playerSessions.Remove(session.playerId);
                }

                session = null;
            }
            else
            {
                handler.BeginReceive(session.buffer, 0, NetLobbySession.BufferSize, SocketFlags.None, new AsyncCallback(ReadCallback), session);
            }

        }
        #endregion


        #region 收到的消息分析
        private void AnalyzeNetMessage(byte[] fullMessageBytes)
        {
            logger.Log("Server - AnalyzeNetMessage");

            bool error = false;
            //反序列化
            try
            {
                int sizeOfUInt32 = sizeof(uint);
                int sizeOfLong = sizeof(long);
                int size = sizeOfUInt32 * 6 + sizeOfLong;
                int count = fullMessageBytes.Length - size;

                ByteStream byteStream = new ByteStream(fullMessageBytes);

                uint length = byteStream.ReadUInt32();
                uint bof = byteStream.ReadUInt32();
                uint l1 = byteStream.ReadUInt32();
                uint l2 = byteStream.ReadUInt32();
                uint l3 = byteStream.ReadUInt32();
                long roomId = byteStream.ReadInt64();
                byte[] content = byteStream.ReadBytes(count);                
                uint eof = byteStream.ReadUInt32();

                if (NetLobbyDefination.NET_MESSAGE_BOF != bof)
                {
                    logger.LogError("AnalyzeNetMessage BOF Error.");
                    error = true;
                }
                else if (NetLobbyDefination.NET_MESSAGE_EOF != eof)
                {
                    logger.LogError("AnalyzeNetMessage EOF Error.");
                    error = true;
                }
                else if (null == content || content.Length == 0)
                {
                    logger.LogError("AnalyzeNetMessage Content Length Error.");
                    error = true;
                }

                if (!error)
                {
                    ReceiveNetMessage(fullMessageBytes, roomId, l1, l2, l3);
                }
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }

        private void ReceiveNetMessage(byte[] fullMessageBytes, long roomId, uint l1, uint l2, uint l3)
        {
            logger.Log("Server - ReceiveNetMessage");

            uint v = l1 & MessageLevelDefinition.MASK_CLIENT_L1_BIZ;
            if (v == MessageLevelDefinition.CLIENT_L1_BIZ)
            {
                ReceiveNetMessage_Business(fullMessageBytes, roomId, l1, l2, l3);

                return;
            }

            switch (l1)
            {
                case MessageLevelDefinition.SERVER_L1_SYSTEM:
                    ReceiveNetMessage_System(fullMessageBytes, roomId, l1, l2, l3);
                    break;
                case MessageLevelDefinition.CLIENT_L1_ROOM:
                    ReceiveNetMessage_Room(fullMessageBytes, roomId, l1, l2, l3);
                    break;
                default:
                    logger.LogError("ReceiveNetMessage : Unkonwn L1 Type");
                    break;                 
            }
        }

        private void ReceiveNetMessage_Business(byte[] fullMessageBytes, long roomId, uint l1, uint l2, uint l3)
        {
            //不做任何处理，直接放入
            if (rooms.ContainsKey(roomId))
            {
                lock (roomsLock)
                {
                    rooms[roomId].Enqueue(fullMessageBytes);
                }
            }
        }

        private void ReceiveNetMessage_Room(byte[] fullMessageBytes, long roomId, uint l1, uint l2, uint l3)
        {
            if (l2 == MessageLevelDefinition.CLIENT_L2_ROOM_CREATE)
            {
                if (l3 == MessageLevelDefinition.CLIENT_L3_ROOM_CREATE_REQ)
                {
                    CreateRoom(fullMessageBytes);
                    return;
                }
            }

            if (l2 == MessageLevelDefinition.CLIENT_L2_ROOM_LIST)
            {
                if (l3 == MessageLevelDefinition.CLIENT_L3_ROOM_LIST_GET_REQ)
                {
                    SendRoomListTo(fullMessageBytes);
                    return;
                }
            }

            if (l2 == MessageLevelDefinition.CLIENT_L2_ROOM_ACTION)
            {
                switch (l3)
                {
                    case MessageLevelDefinition.CLIENT_L3_ROOM_ENTER_REQ:
                        RoomEnter(fullMessageBytes, roomId);
                        break;
                    case MessageLevelDefinition.CLIENT_L3_ROOM_EXIT_REQ:
                        RoomExit(fullMessageBytes, roomId);
                        break;
                    case MessageLevelDefinition.CLIENT_L3_ROOM_START_REQ:
                        RoomStart(fullMessageBytes, roomId);
                        break;
                    case MessageLevelDefinition.CLIENT_L3_ROOM_CLOSE_REQ:
                        RoomClose(fullMessageBytes, roomId);
                        break;
                }
            }
        }

        private void ReceiveNetMessage_System(byte[] fullMessageBytes, long roomId, uint l1, uint l2, uint l3)
        {
            logger.Log("Server - ReceiveNetMessage_System");

            if (l2 == MessageLevelDefinition.SERVER_L2_SYSTEM_PLAYERID)
            {
                if (l3 == MessageLevelDefinition.CLIENT_L3_SYSTEM_PLAYERID_CFM)
                {
                    PlayerIdClientConfirm(fullMessageBytes);
                    return;
                }
            }
        }
        #endregion

        #region PlayerId Confirm
        private void PlayerIdClientConfirm(byte[] fullMessageBytes)
        {
            logger.Log("Server - PlayerIdClientConfirm");

            NetMessage<MC_PlayerId> netMessage = MessageSerialize.Deserialize<MC_PlayerId>(fullMessageBytes);

            MC_PlayerId setPlayerId = netMessage.MessageContent;

            if (setPlayerId.idFromClient == setPlayerId.idFromServer)
            {
                if (playerSessions.ContainsKey(setPlayerId.idFromServer))
                {
                    //发送时不允许修改，修改时不允许发送
                    lock (sessionsLock)
                    {
                        playerSessions[setPlayerId.idFromServer].playerCode = setPlayerId.codeFromClient;
                        playerSessions[setPlayerId.idFromServer].playerIdConfirmed = true;

                        logger.LogFormat("PlayerIdClientConfirm : {0}, {1}, {2}", setPlayerId.idFromClient, setPlayerId.idFromServer, setPlayerId.codeFromClient);
                    }
                }
                else
                {
                    logger.LogError("PlayerIdClientConfirm : 没有对应的playerId");
                }
            }
            else
            {
                logger.LogError("PlayerIdClientConfirm : 客户端返回的Id确认信息与服务器发送的不匹配");
            }            
        }
        #endregion

        #region Room Action
        private void CreateRoom(byte[] fullMessageBytes)
        {
            logger.Log("Server.CreateRoom");

            long playerId = 0;
            long roomId = 0;
            NetLobbySession session = null;
            uint actionResult = 1;

            NetMessage<MC_RoomCreate> netMessage = MessageSerialize.Deserialize<MC_RoomCreate>(fullMessageBytes);
            MC_RoomCreate roomCreate = netMessage.MessageContent;
            playerId = roomCreate.playerId;

            if (rooms.Count > maxRoomCount)
            {
                logger.LogError("CreateRoom : rooms.Count > maxRoomCount");
                actionResult = 1001;
            }            
            else if (playerSessions.ContainsKey(playerId))
            {
                session = playerSessions[playerId];
                if (!session.playerIdConfirmed)
                {
                    logger.LogErrorFormat("CreateRoom : player id {0} not confirmed ", playerId);
                    actionResult = 1002;
                }
                else if (session.playerInRoom)
                {
                    logger.LogErrorFormat("CreateRoom : player {0} already in room ", playerId);
                    actionResult = 1003;
                }
                else
                {
                    roomId = NetLobbyRoom.GetNextRoomId();
                    NetLobbyRoom room = new NetLobbyRoom(session, roomId, logger);

                    rooms.Add(roomId, room);
                }
            }
            else
            {
                logger.LogErrorFormat("CreateRoom : couldn't find player id {0}", playerId);
                actionResult = 1004;
            }

            SendRoomCreateResponse(playerId, roomId, actionResult, session);

            if (actionResult == 1)
            {
                lock (roomChangedLock)
                {
                    roomChanged = true;
                }
            }
        }

        private void SendRoomCreateResponse(long playerId, long roomId, uint actionResult, NetLobbySession session)
        {
            MC_RoomCreate rc = new MC_RoomCreate();
            rc.playerId = playerId;
            rc.roomId = roomId;
            rc.actionResult = actionResult;

            NetMessage<MC_RoomCreate> netMessage = new NetMessage<MC_RoomCreate>(
                roomId,
                MessageLevelDefinition.CLIENT_L1_ROOM,
                MessageLevelDefinition.CLIENT_L2_ROOM_CREATE,
                MessageLevelDefinition.SERVER_L3_ROOM_CREATE_RES,
                rc);

            byte[] bytes = MessageSerialize.Serialize(netMessage);

            session.playerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, new AsyncCallback(SendRoomCreateResponseCallback), session);
        }

        private void SendRoomCreateResponseCallback(IAsyncResult ar)
        {
            try
            {
                NetLobbySession session = (NetLobbySession)ar.AsyncState;
                Socket handler = session.playerSocket;

                handler.EndSend(ar);
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }

        private void RoomEnter(byte[] fullMessageBytes, long roomId)
        {
            long playerId = 0;           

            NetMessage<MC_RoomAction> netMessage = MessageSerialize.Deserialize<MC_RoomAction>(fullMessageBytes);
            MC_RoomAction roomAction = netMessage.MessageContent;

            playerId = roomAction.playerId;
            if (!playerSessions.ContainsKey(playerId))
            {
                logger.LogErrorFormat("RoomEnter : player not found {0}", playerId);
                return;
            }

            uint actionResult = 1;
            NetLobbySession session = null;
            session = playerSessions[playerId];
            if (!session.playerIdConfirmed)
            {
                logger.LogErrorFormat("RoomEnter : player id not confirm {0}", playerId);
                return;
            }

            if (rooms.ContainsKey(roomId))
            {
                NetLobbyRoom room = rooms[roomId];

                if (room.PlayerCount == room.MaxPlayerCount)
                {
                    logger.LogError("RoomEnter : room playerCount full");
                    actionResult = 1001;
                }

                if (room.PlayerSessions.ContainsKey(playerId))
                {
                    logger.LogError("RoomEnter : player already in this room");
                    actionResult = 1002;
                }

                if (room.RoomStarted)
                {
                    logger.LogError("RoomEnter : room already stated");
                    actionResult = 1003;
                }

                if (actionResult == 1)
                {
                    roomAction.roomId = roomId;
                    room.EnterRoom(session);
                }
            }
            else
            {
                actionResult = 0;
            }

            roomAction.actionResult = actionResult;
            SendRoomEnterResponse(roomAction, session);

            if (actionResult == 1)
            {
                lock (roomChangedLock)
                {
                    roomChanged = true;
                }
            }
        }

        private void SendRoomEnterResponse(MC_RoomAction roomAction, NetLobbySession session)
        {
            NetMessage<MC_RoomAction> netMessage = new NetMessage<MC_RoomAction>(
                roomAction.roomId,
                MessageLevelDefinition.CLIENT_L1_ROOM,
                MessageLevelDefinition.CLIENT_L2_ROOM_ACTION,
                MessageLevelDefinition.SERVER_L3_ROOM_ENTER_RES,
                roomAction);

            byte[] bytes = MessageSerialize.Serialize(netMessage);

            session.playerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, new AsyncCallback(SendRoomEnterResponseCallback), session);
        }

        private void SendRoomEnterResponseCallback(IAsyncResult ar)
        {
            try
            {
                NetLobbySession session = (NetLobbySession)ar.AsyncState;
                Socket handler = session.playerSocket;

                handler.EndSend(ar);
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }

        private void RoomStart(byte[] fullMessageBytes, long roomId)
        {
            //all player in room are start?
            //yes : room start
            //no  : set room player status

            if (!rooms.ContainsKey(roomId))
            {
                logger.LogErrorFormat("RoomStart : room id not found {0}", roomId);
                return;
            }

            long playerId = 0;

            NetMessage<MC_RoomAction> netMessage = MessageSerialize.Deserialize<MC_RoomAction>(fullMessageBytes);
            MC_RoomAction roomAction = netMessage.MessageContent;

            playerId = roomAction.playerId;
            if (!playerSessions.ContainsKey(playerId))
            {
                logger.LogErrorFormat("RoomStart : player not found {0}", playerId);
                return;
            }

            NetLobbySession session = null;
            session = playerSessions[playerId];

            NetLobbyRoom room = rooms[roomId];
            room.RoomPlayerStart(playerId);

            int countOfPlayer = room.PlayerSessions.Count;
            int countOfStart = room.PlayerStart.Count;

            roomAction.attachInfo = 0;//one player start
            if (countOfPlayer == countOfStart && countOfPlayer >= roomMinPlayer)
            {
                room.RoomAllStart(playerId, roomId);
            }

            lock (roomChangedLock)
            {
                roomChanged = true;
            }
        }

        private void RoomExitWithIds(long roomId, long playerId)
        {
            if (!playerSessions.ContainsKey(playerId))
            {
                logger.LogErrorFormat("RoomExit : playerId not found {0}", playerId);
                return;
            }

            NetLobbyRoom room = rooms[roomId];
            if (!room.PlayerSessions.ContainsKey(playerId))
            {
                logger.LogErrorFormat("RoomExit : playerId {0} not in room {1}", playerId, roomId);
                return;
            }

            logger.Log("RoomExitWithIds");

            NetLobbySession session = null;
            session = playerSessions[playerId];
            logger.Log("RoomExitWithIds - .playerSession exit room");
            session.ExitRoom();

            logger.Log("RoomExitWithIds - Room.remove player session");
            room.ExitRoom(session);

            if (room.PlayerCount == 0)
            {
                logger.Log("RoomExitWithIds - remove room");
                lock (roomsLock)
                {                    
                    rooms.Remove(roomId);
                }

                ////最后一个人退出，就没必要通知房间内的人了吧
                //room.CloseRoom(() =>
                //{
                //    room = null;
                //});

                room = null;
            }

            lock (roomChangedLock)
            {
                logger.Log("RoomExitWithIds - roomChanged = true");
                roomChanged = true;
            }
        }

        private void RoomExit(byte[] fullMessageBytes, long roomId)
        {
            if (!rooms.ContainsKey(roomId))
            {
                logger.LogErrorFormat("RoomExit : room id not found {0}", roomId);
                return;
            }

            NetMessage<MC_RoomAction> netMessage = MessageSerialize.Deserialize<MC_RoomAction>(fullMessageBytes);
            MC_RoomAction roomAction = netMessage.MessageContent;

            long playerId = roomAction.playerId;

            RoomExitWithIds(roomId, playerId);
        }

        private void RoomClose(byte[] fullMessageBytes, long roomId)
        {
            if (!rooms.ContainsKey(roomId))
            {
                logger.LogErrorFormat("RoomClose : room id not found {0}", roomId);
                return;
            }

            logger.Log("RoomClose");

            long playerId = 0;

            NetMessage<MC_RoomAction> netMessage = MessageSerialize.Deserialize<MC_RoomAction>(fullMessageBytes);
            MC_RoomAction roomAction = netMessage.MessageContent;

            playerId = roomAction.playerId;
            if (!playerSessions.ContainsKey(playerId))
            {
                logger.LogErrorFormat("RoomClose : couldn't find playerId {0}", playerId);
                return;
            }

            NetLobbyRoom room = rooms[roomId];
            if (!room.PlayerSessions.ContainsKey(playerId))
            {
                logger.LogErrorFormat("RoomClose : playerId {0} not in room {1}", playerId, roomId);
                return;
            }

            if (room.RoomOwner != playerId)
            {
                logger.LogErrorFormat("RoomClose : playerId {0} is not room owner {1}", playerId, roomId);
                return;
            }

            logger.LogFormat("RoomClose : playerId {0}, roomId {1}", playerId, roomId);

            room.CloseRoom(() => {
                foreach (NetLobbySession item in room.PlayerSessions.Values)
                {
                    if (null != item)
                    {
                        item.ExitRoom();
                        room.ExitRoom(item, false);
                    }
                }                
                room.PlayerSessions.Clear();

                room = null;
            });            

            lock (roomsLock)
            {
                rooms.Remove(roomId);
            }

            lock (roomChangedLock)
            {
                roomChanged = true;
            }
        }

        /// <summary>
        /// 给指定用户发送房间信息
        /// </summary>
        /// <param name="fullMessageBytes"></param>
        private void SendRoomListTo(byte[] fullMessageBytes)
        {
            NetMessage<MC_RoomAction> netMessage = MessageSerialize.Deserialize<MC_RoomAction>(fullMessageBytes);
            MC_RoomAction roomAction = netMessage.MessageContent;

            long playerId = roomAction.playerId;
            if (!playerSessions.ContainsKey(playerId))
            {
                logger.LogErrorFormat("SendRoomListTo : couldn't find playerId {0}", playerId);
                return;
            }

            NetLobbySession session = playerSessions[playerId];
            if (!session.playerIdConfirmed)
            {
                logger.LogErrorFormat("SendRoomListTo : player id not confirm {0}", playerId);
                return;
            }

            logger.LogFormat("SendRoomListTo : player id {0}", playerId);

            NetMessage<MC_RoomList> netMessageList = GetCurrentRoomList();
            BoardcastRoomInfos(session, netMessageList);
        }

        private void BoardcastRoomInfos(NetLobbySession session, NetMessage<MC_RoomList> netMessageList)
        {
            byte[] bytes = MessageSerialize.Serialize(netMessageList);

            session.playerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, new AsyncCallback(BoardcastRoomInfosCallback), session);
        }

        private void BoardcastRoomInfosCallback(IAsyncResult ar)
        {
            try
            {
                NetLobbySession session = (NetLobbySession)ar.AsyncState;
                Socket handler = session.playerSocket;

                handler.EndSend(ar);
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }

        private NetMessage<MC_RoomList> GetCurrentRoomList()
        {
            RoomInfo info = null;
            MC_RoomList roomList = new MC_RoomList();

            lock (roomsLock)
            {
                foreach (NetLobbyRoom item in rooms.Values)
                {
                    info = new RoomInfo(item.RoomId, item.RoomOwner, item.PlayerCount, item.RoomStarted);

                    foreach (NetLobbySession s in item.PlayerSessions.Values)
                    {
                        info.playerIds.list.Add(s.playerId);
                        info.playerCodes.list.Add(s.playerCode);
                    }
                    info.playerStartIds.list.AddRange(item.PlayerStart);

                    roomList.roomInfos.list.Add(info);
                }
            }//lock

            NetMessage<MC_RoomList> netMessageList = new NetMessage<MC_RoomList>(
                0,
                MessageLevelDefinition.CLIENT_L1_ROOM,
                MessageLevelDefinition.CLIENT_L2_ROOM_LIST,
                MessageLevelDefinition.SERVER_L3_ROOM_LIST_GET_RES,
                roomList);

            return netMessageList;
        }
        #endregion

        #region Heart
        private void BoardcastHeart(NetLobbySession session)
        {
            MC_Heart heart = new MC_Heart();
            heart.frameCount = m_frameCount;

            NetMessage<MC_Heart> netMessage = new NetMessage<MC_Heart>(
                0L,//RoomId，但心跳信息不需要房间编号
                MessageLevelDefinition.SERVER_L1_SYSTEM,
                MessageLevelDefinition.SERVER_L2_SYSTEM_HEART,
                MessageLevelDefinition.SERVER_L3_SYSTEM_HEART,
                heart);

            byte[] bytes = MessageSerialize.Serialize(netMessage);

            //DEBUG; Comment it;
            //StringBuilder s = new StringBuilder();
            //for (int i = 0; i < bytes.Length; i++)
            //{
            //    s.Append(bytes[i]);
            //}
            //logger.Log(s.ToString());

            session.playerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, new AsyncCallback(SendHeartCallback), session);
        }

        private void SendHeartCallback(IAsyncResult ar)
        {
            try
            {
                NetLobbySession session = (NetLobbySession)ar.AsyncState;

                Socket handler = session.playerSocket;
                handler.EndSend(ar);

                //logger.LogFormat("Send Heart done. : {0} - {1}", session.playerId, m_frameCount);                
            }
            catch (Exception e)
            {
                logger.LogError(e.Message);
            }
        }

        void SendPlayerId(NetLobbySession session)
        {
            logger.Log("(out)Command to client to Confirm id.");

            MC_PlayerId setPlayerId = new MC_PlayerId();
            setPlayerId.idFromServer = session.playerId;

            NetMessage<MC_PlayerId> netMessage = new NetMessage<MC_PlayerId>(
                0L,//RoomId，但该信息发送时用户还没有加入任何房间，所以不需要房间编号
                MessageLevelDefinition.SERVER_L1_SYSTEM,
                MessageLevelDefinition.SERVER_L2_SYSTEM_PLAYERID,
                MessageLevelDefinition.SERVER_L3_SYSTEM_PLAYERID_CMD,
                setPlayerId);

            byte[] bytes = MessageSerialize.Serialize(netMessage);

            session.playerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, new AsyncCallback(SendPlayerIdCallback), session.playerSocket);
        }

        void SendPlayerIdCallback(IAsyncResult ar)
        {
            logger.Log("(out)Command to client to Confirm id end.");

            try
            {
                Socket handler = (Socket)ar.AsyncState;

                handler.EndSend(ar);
            }
            catch (Exception e)
            {
            }
        }
        #endregion

        #region Send Thread
        DateTime lastSendHeartTime;
        TimeSpan elapsedSpan;
        long elapsedTicks = 0L;
        CancellationTokenSource cancelToket = new CancellationTokenSource();
        bool tempRoomChanged = false;
        NetMessage<MC_RoomList> tempNetMessage_RoomList;
        void TimeToSend()
        {
            while (!cancelToket.IsCancellationRequested)
            {
                try
                {
                    elapsedTicks = DateTime.Now.Ticks - lastSendHeartTime.Ticks;
                    elapsedSpan = new TimeSpan(elapsedTicks);

                    if (elapsedSpan.TotalMilliseconds >= heartTimes)
                    {
                        lock (sessionsLock)
                        {
                            foreach (NetLobbySession session in playerSessions.Values)
                            {
                                if (session.playerIdConfirmed)
                                {
                                    //logger.LogFormat("Send Heart to : {0} - {1}", session.playerId, m_frameCount);
                                    //Debug; remove comment;
                                    BoardcastHeart(session);
                                }
                            }//for sessions
                        }//lock sessions

                        lastSendHeartTime = DateTime.Now;                        
                    }

                    m_frameCount++;

                    lock (roomsLock)
                    {
                        //每个房间独自广播信息
                        foreach (NetLobbyRoom room in rooms.Values)
                        {
                            room.Boardcast();
                        }
                    }

                    //为避免在lock(sessionLock)中再lock(roomChangeLocd)；不知道会不会死锁
                    //先在外部把内容准备好
                    lock (roomChangedLock)
                    {
                        tempRoomChanged = roomChanged;

                        if (roomChanged)
                        {
                            roomChanged = false;
                        }
                    }

                    if (tempRoomChanged)
                    {
                        tempNetMessage_RoomList = GetCurrentRoomList();
                        //如果房间信息变化了，则广播房间变化信息
                        logger.Log("BoardcastRoomInfos");
                        lock (sessionsLock)
                        {
                            foreach (NetLobbySession session in playerSessions.Values)
                            {
                                if (session.playerIdConfirmed)
                                {
                                    BoardcastRoomInfos(session, tempNetMessage_RoomList);
                                }
                            }
                        }//lock sessions
                    }

                    Thread.Sleep(threadSleep);
                }
                catch (Exception e)
                {
                    logger.LogError("主线程错误：");
                    logger.LogError(e.Message);
                }
            }//while

            logger.Log("Server Stoped. Stop Sending.");
        }//TimeToSend()        
        #endregion
    }
}